<?php
if (!defined('APP_ID')) { die('No direct script access allowed'); }

/**
 * Sending email
 */
class Email {
	private $_from = array();
	private $_to = array();
	private $_cc = array();
	private $_bcc = array();
	private $_subject = '';
	private $_body = '';
	private $_charset = 'utf-8';
	private $_smtp_host = 'localhost';
	private $_smtp_port = 21;
	private $_smtp_user = '';
	private $_smtp_password = '';
	private $_smtp_on = false;

	const _SMTP_EOL = "\r\n";
	const _SMTP_EOD = '.';

	public function __construct() {
		Config::load('email');

		$this->_charset = Config::get('charset', 'email');
		if (Config::get('type', 'email') == 'smtp') {
			$this->_smtp_host = Config::get('smtp_host', 'email');
			$this->_smtp_port = Config::get('smtp_port', 'email');
			$this->_smtp_user = Config::get('smtp_user', 'email');
			$this->_smtp_password = Config::get('smtp_password', 'email');
			$this->_smtp_on = true;
		}
	}

		/**
	 * Add sender
	 */
	public function from($email, $name = '', $visible = true) {
		$address = array();
		$address['email'] = $email;
		$address['name'] = $name;
		$address['visible'] = $visible;
		$this->_from[] = $address;
		return $this;
	}

	/**
	 * Add recipient
	 */
	public function to($email, $name = '', $visible = true) {
		$address = array();
		$address['email'] = $email;
		$address['name'] = $name;
		$address['visible'] = $visible;
		$this->_to[] = $address;
		return $this;
	}

	/**
	 * Add carbon copy
	 */
	public function cc($email, $name = '', $visible = true) {
		$address = array();
		$address['email'] = $email;
		$address['name'] = $name;
		$address['visible'] = $visible;
		$this->_cc[] = $address;
		return $this;
	}

	/**
	 * Add blind carbon copy
	 */
	public function bcc($email, $name = '', $visible = true) {
		$address = array();
		$address['email'] = $email;
		$address['name'] = $name;
		$address['visible'] = $visible;
		$this->_bcc[] = $address;
		return $this;
	}

	/**
	 * Set subject
	 */
	public function subject($text) {
		$this->_subject = $text;
		return $this;
	}

	/**
	 * Set body
	 */
	public function body($text) {
		$this->_body = $text;
		return $this;
	}

	/**
	 * Set body from file
	 */
	public function bodyFromFile($file_name) {
		$this->body(file_get_contents($file_name));
		return $this;
	}

	/**
	 * Build sender string
	 */
	private function _buildFrom() {
		$result = '';
		foreach ($this->_from as $address) {
			if (empty($address['email'])) { continue; }
			if (!$address['visible']) { continue; }
			if (!empty($result)) { $result .= ', '; }
			if (!empty($address['name'])) {
				$result .= $this->_encode($address['name']) . ' <' . $address['email'] . '>';
			} else {
				$result .= $address['email'];
			}
		}
		return $result;
	}

	/**
	 * Build recipient string
	 */
	private function _buildTo() {
		$result = '';
		foreach ($this->_to as $address) {
			if (empty($address['email'])) { continue; }
			if (!$address['visible']) { continue; }
			if (!empty($result)) { $result .= ', '; }
			if (!empty($address['name'])) {
				$result .= $this->_encode($address['name']) . ' <' . $address['email'] . '>';
			} else {
				$result .= $address['email'];
			}
		}
		return $result;
	}

	/**
	 * Build carbon copy string
	 */
	private function _buildCc() {
		$result = '';
		foreach ($this->_cc as $address) {
			if (empty($address['email'])) { continue; }
			if (!$address['visible']) { continue; }
			if (!empty($result)) { $result .= ', '; }
			if (!empty($address['name'])) {
				$result .= $this->_encode($address['name']) . ' <' . $address['email'] . '>';
			} else {
				$result .= $address['email'];
			}
		}
		return $result;
	}

	/**
	 * Build blind carbon copy string
	 */
	private function _buildBcc() {
		$result = '';
		foreach ($this->_bcc as $address) {
			if (empty($address['email'])) { continue; }
			if (!$address['visible']) { continue; }
			if (!empty($result)) { $result .= ', '; }
			if (!empty($address['name'])) {
				$result .= $this->_encode($address['name']) . ' <' . $address['email'] . '>';
			} else {
				$result .= $address['email'];
			}
		}
		return $result;
	}

	/**
	 * Build headers string
	 */
	private function _buildHeaders() {
		$result = '';

		$from = $this->_buildFrom();
		$to = $this->_buildTo();
		$cc = $this->_buildCc();
		$bcc = $this->_buildBcc();

		if (!empty($from)) {
			$result .= 'From: ' . $from . self::_SMTP_EOL;
		}

		if (!empty($from)) {
			$result .= 'To: ' . $to . self::_SMTP_EOL;
		}

		if (!empty($cc)) {
			$result .= 'Cc: ' . $cc . self::_SMTP_EOL;
		}

		if (!empty($bcc)) {
			$result .= 'Bcc: ' . $bcc . self::_SMTP_EOL;
		}

		$result .= 'MIME-Version: 1.0' . self::_SMTP_EOL;
		$result .= 'Content-type: text/html; charset=' . $this->_charset . self::_SMTP_EOL;
		$result .= 'Content-Transfer-Encoding: base64' . self::_SMTP_EOL;
		$result .= 'X-Mailer: PHP/' . phpversion();

		return $result;
	}

	/**
	 * Encoding string
	 */
	private function _encode($in_str, $is_body = false) {
		$out_str = $in_str;
		if ($out_str) {
			if ($is_body) {
				$length = 68;
				$end = '';
				$start = '';
				$spacer = self::_SMTP_EOL;
			} else {
				// define start delimimter, end delimiter and spacer
				$end = '?=';
				$start = '=?' . $this->_charset . '?B?';
				$spacer = $end . self::_SMTP_EOL . $start;

				// determine length of encoded text within chunks
				// and ensure length is even
				$length = 75 - strlen($start) - strlen($end);

				/*
					[EDIT BY danbrown AT php DOT net: The following
					is a bugfix provided by (gardan AT gmx DOT de)
					on 31-MAR-2005 with the following note:
					"This means: $length should not be even,
					but divisible by 4. The reason is that in
					base64-encoding 3 8-bit-chars are represented
					by 4 6-bit-chars. These 4 chars must not be
					split between two encoded words, according
					to RFC-2047.
				*/
				$length = $length - ($length % 4);
			}

			// encode the string and split it into chunks
			// with spacers after each chunk
			$out_str = base64_encode($out_str);
			$out_str = chunk_split($out_str, $length, $spacer);

			// remove trailing spacer and
			// add start and end delimiters
			$spacer = preg_quote($spacer);
			$out_str = preg_replace('/' . $spacer . '$/', '', $out_str);
			$out_str = $start . $out_str . $end;
		}
		return $out_str;
	}

	/**
	 * Replace placeholders with values
	 */
	public function replaceArray($arr) {
		foreach ($arr as $key => $val) {
			$this->replace($key, $val);
		}
	}

	/**
	 * Replace placeholder with value
	 */
	public function replace($var, $val) {
		$this->_body = str_replace('{{' . $var . '}}', $val, $this->_body);
	}

	/**
	 * Perform sending
	 */
	public function send($clear = true) {
		$result = false;

		if ($this->_smtp_on) {
			$result = $this->_smtp_send();
		} else {
			$result = $this->_send();
		}

		if ($clear) {
			$this->clear();
		}

		return $result;
	}

	/**
	 * Clear object
	 */
	public function clear() {
		$this->_from = array();
		$this->_to = array();
		$this->_cc = array();
		$this->_bcc = array();
		$this->_subject = '';
		$this->_body = '';
	}

	/**
	 * Perform sending
	 */
	private function _send() {
		$result = false;

		$headers = $this->_buildHeaders();
		$subject = $this->_encode($this->_subject);
		$body = $this->_encode($this->_body, true);

		if (mail($to, $subject, $body, $headers)) {
			$result = true;
		}

		return $result;
	}

	private function _smtp_send() {
		$result = false;

		$headers = $this->_buildHeaders();
		$subject = $this->_encode($this->_subject);
		$body = $this->_encode($this->_body, true);

		try {
			$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
			if ($socket < 0) {
				throw new Exception('socket_create() failed: ' . socket_strerror(socket_last_error()));
			}

			$connected = socket_connect($socket, $this->_smtp_host, $this->_smtp_port);
			if ($connected === false) {
				throw new Exception('socket_connect() failed: ' . socket_strerror(socket_last_error()));
			}

			$this->_smtpAnswer($socket);

			$this->_smtpResponse($socket, 'EHLO '.$this->_smtp_user);
			$this->_smtpAnswer($socket);

			$this->_smtpResponse($socket, 'AUTH LOGIN');
			$this->_smtpAnswer($socket);

			$this->_smtpResponse($socket, base64_encode($this->_smtp_user));
			$this->_smtpAnswer($socket);

			$this->_smtpResponse($socket, base64_encode($this->_smtp_password));
			$this->_smtpAnswer($socket);

			foreach ($this->_from as $smtp_from) {
				$this->_smtpResponse($socket, 'MAIL FROM:<' . $smtp_from['email'] . '>');
				$this->_smtpAnswer($socket);
				break;
			}

			foreach ($this->_to as $smtp_to) {
				$this->_smtpResponse($socket, 'RCPT TO:<' . $smtp_to['email'] . '>');
				$this->_smtpAnswer($socket);
			}

			foreach ($this->_cc as $smtp_cc) {
				$this->_smtpResponse($socket, 'RCPT TO:<' . $smtp_cc['email'] . '>');
				$this->_smtpAnswer($socket);
			}

			foreach ($this->_bcc as $smtp_bcc) {
				$this->_smtpResponse($socket, 'RCPT TO:<' . $smtp_bcc['email'] . '>');
				$this->_smtpAnswer($socket);
			}

			$this->_smtpResponse($socket, 'DATA');
			$this->_smtpAnswer($socket);

			if (!empty($headers)) {
				$this->_smtpResponse($socket, $headers);
			}
			$this->_smtpResponse($socket, 'Subject:' . $subject . self::_SMTP_EOL);
			$this->_smtpResponse($socket, $body . self::_SMTP_EOL . self::_SMTP_EOD);
			$this->_smtpAnswer($socket);

			$this->_smtpResponse($socket, 'QUIT');
			$this->_smtpAnswer($socket);

			$result = true;
		} catch (Exception $e) {}

		if (isset($socket)) {
			socket_close($socket);
		}

		return $result;
	}

	private function _smtpAnswer($socket) {
		$read = socket_read($socket, 1024);

		//echo $read, '<br />';

		if ($read{0} != '2' && $read{0} != '3') {
			if (!empty($read)) {
				throw new Exception('SMTP error: ' . $read);
			} else {
				throw new Exception('Unknown SMTP error');
			}
		}
	}

	private function _smtpResponse($socket, $message) {
		$message .= self::_SMTP_EOL;
		socket_write($socket, $message, strlen($message));
	}
}